package com.kingyea.mobilepolice.apimanage;

import com.alibaba.fastjson.JSON;
import com.kingyea.mobilepolice.apimanage.accesscontrol.handle.ControllerHandle;
import com.kingyea.mobilepolice.apimanage.accesscontrol.handle.ObjectData;
import com.kingyea.mobilepolice.apimanage.accesscontrol.handle.SecurityService;
import com.kingyea.mobilepolice.apimanage.association.bean.ServiceRequesterAssociation;
import com.kingyea.mobilepolice.apimanage.association.service.ServiceRequesterAssociationService;
import com.kingyea.mobilepolice.apimanage.log.OperateType;
import com.kingyea.mobilepolice.apimanage.log.OperationLogAnnotation;
import com.kingyea.mobilepolice.apimanage.log.OperationLogAspect;
import com.kingyea.mobilepolice.apimanage.log.bean.InvokLog;
import com.kingyea.mobilepolice.apimanage.log.service.InvokLogService;
import com.kingyea.mobilepolice.apimanage.paraminfo.bean.ParamInfo;
import com.kingyea.mobilepolice.apimanage.paraminfo.service.ParamInfoService;
import com.kingyea.mobilepolice.apimanage.requester.bean.Requester;
import com.kingyea.mobilepolice.apimanage.requester.service.RequesterService;
import com.kingyea.mobilepolice.apimanage.serviceinfo.bean.ServiceInfo;
import com.kingyea.mobilepolice.apimanage.serviceinfo.service.ServiceInfoService;
import com.kingyea.mobilepolice.apimanage.system.sync.InterfaceFlowTask;
import com.kingyea.mobilepolice.apimanage.system.sync.ServerApiMonitorTask;
import com.kingyea.mobilepolice.apimanage.sysvo.bean.RequestObj;
import com.kingyea.mobilepolice.apimanage.utils.*;
import com.kingyea.mobilepolice.apimanage.utils.ws.WSUtils;
import com.kingyea.mobilepolice.system.Result;
import com.kingyea.mobilepolice.system.exception.BusinessException;
import com.kingyea.mobilepolice.user.bean.User;
import com.kingyea.mobilepolice.user.client.UserServiceClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * 网关入口
 *
 * @author Mr.Lin
 * @version v1.0.0
 * @description
 * @date Created in 2018/2/23 15:09
 */
@Api(value = "网关入口接口", description = " 网关入口 相关文档 ")
@RequestMapping(value = "/api")
@RestController
public class Controller {


    private static final Logger LOGGER = LoggerFactory.getLogger(Controller.class);
    @Autowired
    private ServiceRequesterAssociationService serviceRequesterAssociationService;
    @Autowired
    private RequesterService requesterService;
    @Autowired
    private ServiceInfoService serviceInfoService;
    @Autowired
    private ParamInfoService paramInfoService;
    @Autowired
    private InvokLogService invokLogService;
    @Autowired
    private SecurityService securityService;
    @Autowired
    private ControllerHandle controlHandle;

    @Autowired
    private InterfaceFlowTask interfaceFlowTask;

    @Autowired
    private ServerApiMonitorTask serverApiMonitorTask;

    @Autowired
    private UserServiceClient userServiceClient;

    /**
     * 所有请求入口
     *
     * @param map
     * @param request
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "所有请求入口", notes = "所有请求入口 ", httpMethod = "POST", response = Result.class)
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "requestId", value = "请求方ID", required = true, dataType = "int")
            , @ApiImplicitParam(name = "serviceId", value = "接口服务ID", required = true, dataType = "int")
            , @ApiImplicitParam(name = "params", value = "业务参数", required = true, dataType = "JSON")
    })
    @OperationLogAnnotation(operateName = "接口调用", operateType = OperateType.SELECT, module = "网关")
    @RequestMapping(value = "/**", method = RequestMethod.POST)
    public Object APIInvoke(@RequestBody Map<String, Object> map, HttpServletRequest request) throws Exception {
        if (GlobalVar.usingEncrypt) {// 是否使用加密
            map = securityService.decryptData(map);
        }

        Long startTime = System.currentTimeMillis();
        Object result = null;
        /** 接收到请求，准备调用日志 */
        InvokLog invokLog = new InvokLog();
        invokLog.setTerminalId(IpUtil.getRealIp(request));
        invokLog.setInvokTime(new Date());
        invokLog.setInvokCondition(JSON.toJSONString(map));// 请求参数

        //得到请求定ip
        String ip = getIpaddress(request);
        map.put("reqIp", ip);
        LOGGER.info("ipaddres :" + ip);
        if (GlobalVar.usingCheck) {
            ObjectData objectData = controlHandle.usingAccessControl(map);
            if (!objectData.isSuccess()) {
                throw new BusinessException(StateCode.ACCESS_CONTROL_ERROR, objectData.getMessage());
            }
            LOGGER.info("访问控制校验通过！！");
        }

        ServiceRequesterAssociation association = null;
        Requester requester = null;
        ServiceInfo serviceInfo = null;
        RequestObj reqObj = null;

        Exception exception = null;
        try {
            String servletPath = request.getServletPath();
            LOGGER.info(" ok,GlobalVar.inside=" + GlobalVar.inside + "： " + servletPath + "  " + JSON.toJSONString(map));

            // 检查参数格式(  )
            reqObj = JSON.parseObject(JSON.toJSONString(map), RequestObj.class);
            if (reqObj == null || (NumberUtils.isNullOrLEZero(reqObj.getRequestId()) && StringUtils.isEmpty(reqObj.getRequestSN()))
                    || (NumberUtils.isNullOrLEZero(reqObj.getServiceId()) && StringUtils.isEmpty(reqObj.getServiceSN()))) {
                throw new BusinessException(StateCode.NO_ARGUMENT_ERROR);
            }

            // 检查参数数据是否合法，查找关联信息是否存在并且审核通过
            if (!NumberUtils.isNullOrLEZero(reqObj.getRequestId()) && !NumberUtils.isNullOrLEZero(reqObj.getServiceId())) {// ( 通过ID查询或者SN码查询 )
                association = serviceRequesterAssociationService.getById(reqObj.getRequestId(), reqObj.getServiceId());
            } else {
                association = serviceRequesterAssociationService.getBySN(reqObj.getRequestSN(), reqObj.getServiceSN());
            }

            if (association != null && association.getStatus().intValue() == 3) {
                // 查询用户信息
                requester = requesterService.getById(association.getRequesterId());
                // 查询接口信息
                serviceInfo = serviceInfoService.getById(association.getServiceId());

                if (NumberUtils.isNullOrLEZero(reqObj.getServiceId())) {
                    reqObj.setServiceId(serviceInfo.getId());
                }
                if (NumberUtils.isNullOrLEZero(reqObj.getRequestId())) {
                    reqObj.setRequestId(requester.getId());
                }

                // 检查url是否正确
                String controllerUrl = Controller.class.getAnnotation(RequestMapping.class).value()[0];
                String publicPaht1 = servletPath.substring(servletPath.indexOf(controllerUrl) + controllerUrl.length());
                if (!publicPaht1.equals(serviceInfo.getPublicPath())) {
                    throw new BusinessException(StateCode.INVOKE_PATH_ERR);
                }

                if (requester != null && serviceInfo != null) {
                    if (requester.getStatus().intValue() != 3) {
                        throw new BusinessException(StateCode.ILLEGAL_USER_ERROR);
                    }
                    if (serviceInfo.getStatus().intValue() != 3) {
                        throw new BusinessException(StateCode.ILLEGAL_INVOKE_API_NOT_AUTHORIZATION);
                    }
                    if (serviceInfo.getIsOnline().intValue() != 1 || serviceInfo.getIsDel().intValue() != 0) {
                        throw new BusinessException(StateCode.ILLEGAL_INVOKE_API_NOT_ONLINE);
                    }
                } else throw new BusinessException(StateCode.SERVER_DATA_MISSING_ERROR);
                // 权限参数校验到此为止
            } else {
                throw new BusinessException(StateCode.ILLEGAL_INVOKE);// 映射关系不存在或未授权
            }


            /** 异步请求流量收集 */
            interfaceFlowTask.requestFlowCollect(reqObj);

            if (serviceInfo.getApiLocation().intValue() != GlobalVar.inside.intValue()) {// 接口所在域 != 当前网域
                // 转到边界另一边 参数原封不动转过去，post


//                        // 如果转到另一边，需要经过zuul网关，拼接完整的地址：http://host:port/zuulpath/publicpath
//                        serviceInfo.setPublicPath(GlobalVar.SERVICE_GATEWAY_PATH+serviceInfo.getPublicPath());//加上zuul网关地址

                Map<String, String> headers = new HashMap<String, String>() {{
                    put("Content-Type", "application/json");
                }};
                //# 1网外 2网内
                if (GlobalVar.inside.intValue() == GlobalVar.insideWai) {//如果当前是网外，那就转发给网内处理
                    String resultStr = HttpUtil.invokeUrl(GlobalVar.insideAddress + "/api" + servletPath, map, headers, HttpMethod.POST);
                    try {
                        result = JSON.parse(resultStr);
                    } catch (Throwable e) {
                        result = Result.getSuccessResult(resultStr);
                    }
                } else if (GlobalVar.inside.intValue() == GlobalVar.insideNei) {
//                            String resultStr = HttpUtil.invokeUrl(GlobalVar.address1 + servletPath, map, headers, HttpUtil.HttpMethod.POST);
//                            if (StringUtils.isEmpty(resultStr)) {
//                                return Result.getFailedResult(resultStr);
//                            }
//                            result = JSON.parse(resultStr);
                    result = Result.getFailedResult("接口调用失败，局域网无法对外发起请求");
                } else {
                    throw new BusinessException(StateCode.SYSTEM_ERROR);
                }
            } else {
                String serviceAddress = (serviceInfo.getServiceAddress().toLowerCase().startsWith("http") ? serviceInfo.getServiceAddress() : "http://" + serviceInfo.getServiceAddress());
                String path = serviceInfo.getPath().startsWith("/") ? serviceInfo.getPath() : "/" + serviceInfo.getPath();
                String apiAddress = serviceAddress + ":" + serviceInfo.getPort() + path;

                ParamInfo queryObj = new ParamInfo();
                queryObj.setServiceId(serviceInfo.getId());
                List<ParamInfo> paramInfos = paramInfoService.list(queryObj);
                /** 判断是webservice还是http+json */
                if (serviceInfo.getApiType().intValue() == 1) {//http+json
//                            String resultStr = HttpUtil.invokeUrl2(apiAddress, paramInfos, reqObj.getParams(), HttpMethod.resolve(serviceInfo.getHttpMethod()));
//                            Object resmap = JSON.parse(resultStr);
                    Object resmap = HttpUtil.invokeUrlByRestTemplate(apiAddress, paramInfos, reqObj.getParams(), HttpMethod.resolve(serviceInfo.getHttpMethod())).getBody();
                    result = Result.getSuccessResult(resmap);
                } else if (serviceInfo.getApiType().intValue() == 2) {//webservice
//                            System.out.println(JSON.toJSONString(paramInfos));
                    paramInfos.sort(Comparator.comparing(ParamInfo::getPosition));
                    /** paramInfos 必须所有参数都描述都录入，排序后，必须1开始 */
                    if (paramInfos != null && paramInfos.size() > 0) {
                        if (paramInfos.get(0).getPosition().intValue() != 1) {
                            throw new BusinessException(StateCode.SERVER_DATA_ERROR_ARGUMENT_RECORD_ERROR);
                        }
                        if (paramInfos.get(paramInfos.size() - 1).getPosition().intValue() != paramInfos.size()) {
                            throw new BusinessException(StateCode.SERVER_DATA_ERROR_ARGUMENT_RECORD_ERROR);
                        }
                    }

                    Map<String, Object> paramsMap = reqObj.getParams();
                    List queryParams = new ArrayList();
                    for (ParamInfo paramInfo : paramInfos) {
                        if (paramsMap.containsKey(paramInfo.getName())) {
                            queryParams.add(typeConversion(paramInfo.getType(), paramsMap.get(paramInfo.getName())));
                        } else if (paramInfo.getIsRequired().intValue() == 1) {
                            throw new BusinessException(StateCode.NO_ARGUMENT_ERROR);
                        } else {
                            queryParams.add(null);
                        }
                    }

//                            System.out.println(JSON.toJSONString(queryParams)+"");

                    if (StringUtils.isEmpty(serviceInfo.getInvokMethod())) {
                        throw new BusinessException(StateCode.SERVER_DATA_ERROR_INVOKE_TARGET_MISSING);
                    }
                    Object resultObj = WSUtils.query(apiAddress, serviceInfo.getInvokMethod(), queryParams.toArray());

                    result = Result.getSuccessResult(resultObj);
                } else {
                    throw new BusinessException(StateCode.SERVER_DATA_ERROR_ILLEGAL_API_TYPE);
                }
            }
            invokLog.setResultStatus(OperationLogAspect.SUCCESS);
            ObjectData objectData = controlHandle.dataSizeCheck(result, reqObj.getServiceId());

            if (!objectData.isSuccess()) {
                throw new BusinessException("112121", objectData.getMessage());
            }
            //== 关键字过滤
            result = controlHandle.keywordFilter2(result, serviceInfo.getId());//TODO 加密
            if (GlobalVar.usingEncrypt) {

                result = securityService.encryptData(result);//todo
            }
            /** 异步响应流量收集*/
            interfaceFlowTask.respohnseFlowCollect(reqObj, result);

            return result;
        } catch (Exception e) {
            invokLog.setResultStatus(OperationLogAspect.FAILED);
            exception = e;
            if (result == null) {
                if (e instanceof BusinessException) {
                    BusinessException be = (BusinessException) e;
                    Result res = new Result();
                    res.setCode(be.getCode());
                    res.setMsg(be.getMsg());
                    res.setData(be.getObj());
                    result = res;
                } else {
                    result = Result.getFailedResult("");
                }
            }

            throw e;
        } finally {
            //异步保存服务接口监控记录
            serverApiMonitorTask.saveMonitor(startTime, reqObj, result, request);
            try {
                Result r = JSON.parseObject(JSON.toJSONString(result), Result.class);
                invokLog.setResultCode(r.getCode());
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (serviceInfo != null) {
                invokLog.setServiceId(serviceInfo.getId());
                invokLog.setServiceName(serviceInfo.getName());
                invokLog.setServiceSerialNo(serviceInfo.getSerialNo());
            } else {
                invokLog.setServiceId(reqObj.getServiceId());
                invokLog.setServiceSerialNo(reqObj.getServiceSN());
            }
            if (requester != null) {
                invokLog.setRequesterId(requester.getId());
                invokLog.setRequester(requester.getRequester());
                invokLog.setRequesterSerialNo(requester.getSerialNo());
                invokLog.setOrganization(requester.getOrganization());
                invokLog.setOrganizationId(requester.getOrganizationId());
            } else {
                invokLog.setRequesterId(reqObj.getRequestId());
                invokLog.setRequesterSerialNo(reqObj.getRequestSN());
            }
            invokLog.setProcessTime(System.currentTimeMillis() - startTime);// 处理时间
            try {
                Result<User> userResult = userServiceClient.getCurrentUserInfo();
                if (userResult.getCode().equalsIgnoreCase(StateCode.OPERATION_SUCCESS)) {
                    invokLog.setUserId(userResult.getData().getUserId());
                    invokLog.setUserName(userResult.getData().getName());
                }
            } catch (Exception e) {

            }
            invokLogService.asyncSave(invokLog);// todo  以后上线后，换回异步
            if (exception != null) {
                throw exception;
            }
        }
    }

    /**
     * 获取ip地址
     */
    private String getIpaddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }


    /**
     * @param type 参数类型: 1.String 2.int 3.double
     * @param val
     * @return
     */
    public Object typeConversion(int type, Object val) {
        switch (type) {
            case 1:
                val = val.toString();
                break;
            case 2:
                val = NumberUtils.obj2Int(val);
                break;
            case 3:
                val = NumberUtils.obj2Double(val);
                break;
        }
        return val;
    }


}


